package net.rvbr1.gwt.grad.tests.client;

import com.google.gwt.cell.client.Cell.Context;
import com.google.gwt.cell.client.FieldUpdater;
import com.google.gwt.cell.client.NumberCell;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.i18n.client.NumberFormat;
import com.google.gwt.safehtml.shared.SafeHtmlUtils;
import com.google.gwt.user.cellview.client.Column;
import com.google.gwt.user.cellview.client.ColumnSortEvent;
import com.google.gwt.user.cellview.client.ColumnSortList;
import com.google.gwt.user.cellview.client.ColumnSortList.ColumnSortInfo;
import com.google.gwt.user.cellview.client.TextColumn;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.DockLayoutPanel;
import com.google.gwt.user.client.ui.MenuItem;
import com.google.gwt.user.client.ui.RootLayoutPanel;
import com.google.gwt.view.client.NoSelectionModel;

import net.rvb1.gwt.grad.ui.client.cellview.cell.EditTextCell;
import net.rvb1.gwt.grad.ui.client.cellview.cell.SelectionCell;
import net.rvb1.gwt.grad.ui.client.cellview.cell.SelectionCellProvider;
import net.rvb1.gwt.grad.ui.client.cellview.grid.DataGrid;
import net.rvb1.gwt.grad.ui.client.cellview.pager.ScrollPager;
import net.rvb1.gwt.grad.ui.client.menu.ContextMenu;
import net.rvb1.gwt.grad.ui.client.menu.events.BeforeShowContextMenuEvent;
import net.rvbr1.gwt.grad.tests.shared.Employee;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

public class GradTestsEntryPoint implements EntryPoint {

  private final class BossCellProvider implements SelectionCellProvider<Employee> {
    @Override
    public String getLabel(Employee object) {
      return (object == null) ? null : object.getFirstName() + " " + object.getLastName();
    }

    @Override
    public List<Employee> getOptions(Context context) {
      List<Employee> toReturn = new ArrayList<Employee>();

      for (Employee employee : GradTestsEntryPoint.this.employeeList) {
        if (!employee.equals(context.getKey())) {
          toReturn.add(employee);
        }
      }

      return toReturn;
    }
  }

  private static final class BossComparator implements Comparator<Employee> {
    private final BossCellProvider bossCellProvider;

    private BossComparator(BossCellProvider bossCellProvider) {
      this.bossCellProvider = bossCellProvider;
    }

    @Override
    public int compare(Employee o1, Employee o2) {
      if (o1 == null && o2 == null) {
        return 0;
      }

      if (o1 == null && o2 != null) {
        return 1;
      }

      if (o1 != null && o2 == null) {
        return -1;
      }

      if (o1.getBoss() == null && o2.getBoss() == null) {
        return 0;
      }

      if (o1.getBoss() == null && o2.getBoss() != null) {
        return 1;
      }

      if (o1.getBoss() != null && o2.getBoss() == null) {
        return -1;
      }

      return this.bossCellProvider.getLabel(o1.getBoss()).compareTo(
          this.bossCellProvider.getLabel(o2.getBoss()));
    }
  }

  private static final int MAX_EMPLOYEE_POPULATION = 1000;

  protected boolean sorting = false;
  private List<Employee> employeeList = new ArrayList<Employee>();

  private DataGrid<Employee> gDataGrid;

  // private final class EmployeeCellProvider implements SelectionCellProvider<Employee> {
  // public String getLabel(Employee object) {
  // return (object == null) ? null : object.getFirstName() + " " + object.getLastName();
  // }
  //
  // public List<Employee> getOptions(Context context) {
  // return GradTestsEntryPoint.this.employees2;
  // }
  // }

  // private List<Employee> employees2;

  @Override
  public void onModuleLoad() {
    ensureEmployeeListIsComplete();

    // DATAGRID ...
    this.gDataGrid = new DataGrid<Employee>();

    final Column<Employee, Number> idColumn =
        new Column<Employee, Number>(new NumberCell(NumberFormat.getFormat("0"))) {
          @Override
          public Number getValue(Employee employee) {
            return (employee != null) ? employee.getId() : null;
          }
        };
    this.gDataGrid.addColumn(idColumn, this.gDataGrid.new DataGridResizableHeader("Id", idColumn));
    idColumn.setSortable(true);
    this.gDataGrid.setColumnWidth(idColumn, 55, Unit.PX);

    final Column<Employee, String> firstNameColumn =
        new Column<Employee, String>(new EditTextCell()) {
          @Override
          public String getValue(Employee employee) {
            return (employee != null) ? employee.getFirstName() : null;
          }
        };
    this.gDataGrid.addColumn(firstNameColumn, SafeHtmlUtils.fromTrustedString("First&nbsp;Name"));
    firstNameColumn.setFieldUpdater(new FieldUpdater<Employee, String>() {
      @Override
      public void update(int index, Employee employee, String newFirstName) {
        employee.setFirstName(newFirstName);
        GradTestsEntryPoint.this.gDataGrid.redraw();
      }
    });

    final Column<Employee, String> lastNameColumn =
        new Column<Employee, String>(new EditTextCell()) {
          @Override
          public String getValue(Employee employee) {
            return (employee != null) ? employee.getLastName() : null;
          }
        };
    this.gDataGrid.addColumn(lastNameColumn, SafeHtmlUtils.fromTrustedString("Last&nbsp;Name"));
    lastNameColumn.setFieldUpdater(new FieldUpdater<Employee, String>() {
      @Override
      public void update(int index, Employee employee, String newLastName) {
        employee.setLastName(newLastName);
        GradTestsEntryPoint.this.gDataGrid.redraw();
      }
    });

    final BossCellProvider bossCellProvider = new BossCellProvider();
    final SelectionCell<Employee> bossCell = new SelectionCell<Employee>(bossCellProvider, true);
    final Column<Employee, Employee> bossColumn = new Column<Employee, Employee>(bossCell) {
      @Override
      public Employee getValue(Employee rowData) {
        return (rowData != null) ? rowData.getBoss() : null;
      }
    };
    bossColumn.setFieldUpdater(new FieldUpdater<Employee, Employee>() {
      @Override
      public void update(int index, Employee employee, Employee newBoss) {
        employee.setBoss(newBoss);
        GradTestsEntryPoint.this.gDataGrid.redraw();
      }
    });
    bossCell.setWidth(350);
    this.gDataGrid.setColumnWidth(bossColumn, bossCell.getWidth() + 50 + "px");
    this.gDataGrid.addColumn(bossColumn, "Boss");

    final TextColumn<Employee> bossNameColumn = new TextColumn<Employee>() {
      @Override
      public String getValue(Employee employee) {
        return (employee != null) ? bossCellProvider.getLabel(employee.getBoss()) : null;
      }
    };
    this.gDataGrid.addColumn(bossNameColumn, SafeHtmlUtils.fromTrustedString("Boss&nbsp;Name"));

    final ScrollPager pager = new ScrollPager();
    pager.setDisplay(this.gDataGrid);

    // CellWidget<String> cellWidget = new CellWidget<String>(new TextCell());
    // cellWidget.setValue("TEST DE CELLWIDGET");

    // CellWidget<Employee> cellWidget2 = new CellWidget<Employee>(
    // new SelectionCell<Employee>(new EmployeeCellProvider(), true), herve);
    // cellWidget2.setWidth("155px");

    DockLayoutPanel dockLayoutPanel = new DockLayoutPanel(Unit.PX);
    dockLayoutPanel.addSouth(pager, 25);
    // dockLayoutPanel.addNorth(cellWidget2, 30);
    dockLayoutPanel.add(this.gDataGrid);

    RootLayoutPanel.get().add(dockLayoutPanel);
    RootLayoutPanel.get().setWidgetLeftWidth(dockLayoutPanel, 100, Unit.PX, 800, Unit.PX);
    RootLayoutPanel.get().setWidgetTopHeight(dockLayoutPanel, 100, Unit.PX, 500, Unit.PX);

    // FIN DU LAYOUT

    net.rvb1.gwt.grad.ui.client.cellview.pager.ScrollPager.ListDataProvider<Employee> myDataProvider =
        new net.rvb1.gwt.grad.ui.client.cellview.pager.ScrollPager.ListDataProvider<Employee>(pager);

    myDataProvider.getList().addAll(this.employeeList);

    // AsyncDataProvider<Employee> myDataProvider = new AsyncDataProvider<Employee>() {
    // @Override
    // protected void onRangeChanged(HasData<Employee> display) {
    // final ColumnSortList columnSortList = GradTestsEntryPoint.this.gDataGrid.getColumnSortList();
    // if (columnSortList != null && columnSortList.size() == 0) {
    // columnSortList.push(new ColumnSortInfo(idColumn, true));
    // }
    //
    // final ColumnSortInfo sortInfo = columnSortList.get(0);
    // Collections.sort(GradTestsEntryPoint.this.employeeList, new Comparator<Employee>() {
    // public int compare(Employee o1, Employee o2) {
    // if (o1 == null && o2 == null) {
    // return 0;
    // }
    //
    // if (o1 != null && o2 == null) {
    // return sortInfo.isAscending() ? 1 : -1;
    // }
    //
    // if (o1 == null && o2 != null) {
    // return sortInfo.isAscending() ? -1 : 1;
    // }
    //
    // if (sortInfo.getColumn() == idColumn) {
    // if (sortInfo.isAscending()) {
    // return Integer.valueOf(o1.getId()).compareTo(Integer.valueOf(o2.getId()));
    // } else {
    // return Integer.valueOf(o2.getId()).compareTo(Integer.valueOf(o1.getId()));
    // }
    // } else if (sortInfo.getColumn() == firstNameColumn) {
    // if (sortInfo.isAscending()) {
    // return o1.getFirstName().compareTo(o2.getFirstName());
    // } else {
    // return o2.getFirstName().compareTo(o1.getFirstName());
    // }
    // } else if (sortInfo.getColumn() == lastNameColumn) {
    // if (sortInfo.isAscending()) {
    // return o1.getLastName().compareTo(o2.getLastName());
    // } else {
    // return o2.getLastName().compareTo(o1.getLastName());
    // }
    // } else if (sortInfo.getColumn() == bossColumn || sortInfo.getColumn() == bossNameColumn) {
    // if (o1.getBoss() == null && o2.getBoss() == null) {
    // return 0;
    // }
    //
    // if (o1.getBoss() != null && o2.getBoss() == null) {
    // return sortInfo.isAscending() ? 1 : -1;
    // }
    //
    // if (o1.getBoss() == null && o2.getBoss() != null) {
    // return sortInfo.isAscending() ? -1 : 1;
    // }
    //
    // if (sortInfo.isAscending()) {
    // return
    // bossCellProvider.getLabel(o1.getBoss()).compareTo(bossCellProvider.getLabel(o2.getBoss()));
    // } else {
    // return
    // bossCellProvider.getLabel(o2.getBoss()).compareTo(bossCellProvider.getLabel(o1.getBoss()));
    // }
    // }
    // return 0;
    // }
    // });
    //
    // Range range = display.getVisibleRange();
    // // List<String> strings = new ArrayList<String>();
    // // for(int i = 0 ; i < range.getLength(); i++) {
    // // strings.add(String.valueOf(range.getStart() + i));
    // // }
    //
    // final int start = range.getStart();
    // final int end = start + range.getLength();
    // List<Employee> dataInRange = GradTestsEntryPoint.this.employeeList.subList(start, end);
    //
    // updateRowData(display, start, dataInRange);
    // }
    // };
    myDataProvider.addDataDisplay(this.gDataGrid);
    // this.gDataGrid.setRowCount(this.employeeList.size(), true);

    // Sort options applied to the grid :
    // - all the columns are sortable, and
    // - an async ColSortHandler branched to the datagrid is used to refresh this very datagrid
    // every time a sort is triggered
    firstNameColumn.setSortable(true);
    lastNameColumn.setSortable(true);
    bossColumn.setSortable(true);
    bossNameColumn.setSortable(true);

    // AsyncHandler asyncColSortHandler = new AsyncHandler(this.gDataGrid);
    // this.gDataGrid.addColumnSortHandler(asyncColSortHandler);
    final ColumnSortList columnSortList = GradTestsEntryPoint.this.gDataGrid.getColumnSortList();
    if (columnSortList != null && columnSortList.size() == 0) {
      columnSortList.push(new ColumnSortInfo(idColumn, true));
    }
    ColumnSortEvent.ListHandler<Employee> listColSortHandler =
        new ColumnSortEvent.ListHandler<Employee>(myDataProvider.getList());

    listColSortHandler.setComparator(idColumn, new Comparator<Employee>() {
      @Override
      public int compare(Employee o1, Employee o2) {
        if (o1 == null && o2 == null) {
          return 0;
        }

        if (o1 == null && o2 != null) {
          return 1;
        }

        if (o1 != null && o2 == null) {
          return -1;
        }

        return o1.getId() - o2.getId();
      }
    });

    listColSortHandler.setComparator(firstNameColumn, new Comparator<Employee>() {
      @Override
      public int compare(Employee o1, Employee o2) {
        if (o1 == null && o2 == null) {
          return 0;
        }

        if (o1 == null && o2 != null) {
          return 1;
        }

        if (o1 != null && o2 == null) {
          return -1;
        }

        return o1.getFirstName().compareTo(o2.getFirstName());
      }
    });

    listColSortHandler.setComparator(lastNameColumn, new Comparator<Employee>() {
      @Override
      public int compare(Employee o1, Employee o2) {
        if (o1 == null && o2 == null) {
          return 0;
        }

        if (o1 == null && o2 != null) {
          return 1;
        }

        if (o1 != null && o2 == null) {
          return -1;
        }

        return o1.getFirstName().compareTo(o2.getLastName());
      }
    });

    final BossComparator bossComparator = new BossComparator(bossCellProvider);
    listColSortHandler.setComparator(bossColumn, bossComparator);
    listColSortHandler.setComparator(bossNameColumn, bossComparator);
    this.gDataGrid.addColumnSortHandler(listColSortHandler);

    // Adding a context menu to the grid
    ContextMenu menu = new ContextMenu();
    menu.addItem(new MenuItem(SafeHtmlUtils.fromString("Test"), new Scheduler.ScheduledCommand() {
      @Override
      public void execute() {
        Window.alert("Test");
      }
    }));
    menu.addItem(new MenuItem(SafeHtmlUtils.fromTrustedString("Test&nbsp;2"),
        new Scheduler.ScheduledCommand() {
          @Override
          public void execute() {
            Window.alert("Test 2");
          }
        }));
    this.gDataGrid.setContextMenu(menu);

    // Test : nullifying the context menu. The browser's default context menu should appear when
    // asked !
    // this.gDataGrid.setContextMenu(null);

    // Test : replace the current menu with a new one to test a 'live' change ...
    ContextMenu menu2 = new ContextMenu();
    menu2.addItem(new MenuItem(SafeHtmlUtils.fromTrustedString("Test&nbsp;3"),
        new Scheduler.ScheduledCommand() {
          @Override
          public void execute() {
            Window.alert("Test 3");
          }
        }));
    menu2.addItem(new MenuItem(SafeHtmlUtils.fromTrustedString("Test&nbsp;4"),
        new Scheduler.ScheduledCommand() {
          @Override
          public void execute() {
            Window.alert("Test 4");
          }
        }));
    this.gDataGrid.setContextMenu(menu2);

    // Test : do not show the context menu when the user clicks on an even row.
    this.gDataGrid.addBeforeShowContextMenuHandler(new BeforeShowContextMenuEvent.Handler() {
      @Override
      public void onBeforeShowContextMenu(BeforeShowContextMenuEvent event) {
        if (event.getContext().getIndex() % 2 == 0) {
          event.cancel();
        }
      }
    });

    // No line selection ...
    this.gDataGrid.setSelectionModel(new NoSelectionModel<Employee>());

    // Group by boss name
    // this.gDataGrid.groupBy(bossNameColumn);

    // this.gDataGrid.getColumnSortList().push(new ColumnSortInfo(bossNameColumn, true));

    // gDataGrid.setContextualMenu(null);
  }

  private void ensureEmployeeListIsComplete() {
    if (this.employeeList.size() == 0) {
      for (int i = 0; i < MAX_EMPLOYEE_POPULATION; i++) {
        Employee newEmployee = new Employee();
        newEmployee.setId(i);
        newEmployee.setFirstName("First " + i);
        newEmployee.setLastName("Last " + (MAX_EMPLOYEE_POPULATION - i));
        if (i != 0) {
        }
        this.employeeList.add(newEmployee);
      }

      for (int i = 0; i < MAX_EMPLOYEE_POPULATION; i++) {
        this.employeeList.get(i).setBoss(
            this.employeeList.get(Double.valueOf(Math.random() * 5).intValue()));
        // newEmployee.setBoss(i == 0 ? null : this.employeeList.get(i - 1));
      }
    }
  }
}
